//
// This file is part of cpp-lib, a library of C++ functions and classes.
// Cpp-lib is copyright (C) 2004 and onwards, The CPL Interest Group.
// 
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or (at
// your option) any later version.
// 
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
// General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this library (see the file COPYING); if not, write to the
// Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
// 02111-1307  USA
//
// $Id$
//


#include "cpp-lib/command_line.h"

using namespace cpl::util;

cpl::util::command_line::command_line(

  opm_entry const* beg ,
  opm_entry const* end ,
  char const * const * argv_

)
: state( true ) , argv( argv_ ) , props( beg , end )
{

  for( property_map::const_iterator i = props.begin() ;
       i != props.end() ;
       ++i )

    if( short_to_long.count( i->second.short_name ) )

      throw std::runtime_error( "short option name used twice" ) ;

    else

      short_to_long[ i->second.short_name ] = i->first ;


  // skip invocation name

  while( *++argv ) {

    std::string s = *argv ;


    // non-option argument?

    if( s.size() <= 1 ) break ;

    if( s[ 0 ] != '-' ) break ;


    std::string name ;

    if( s.size() == 2 ) {

      // stop option processing on --
      if( s[ 1 ] == '-' ) {
        ++argv ;
        break ;
      }


      // else we have a short option

      std::map< char , std::string >::const_iterator i =
	short_to_long.find( s[ 1 ] ) ;

      if( i == short_to_long.end() )
	throw std::runtime_error( "unknown option: " + s ) ;


      name = i->second ;

    }

    else {

      if( s[ 1 ] != '-' )
	throw std::runtime_error( "sorry: option flags cannot be combined" ) ;

      // long option

      name.assign( s.begin() + 2 , s.end() ) ;

    }


    property_map::const_iterator i = props.find( name ) ;

    if( i == props.end() )
      throw std::runtime_error( "unknown option: " + s ) ;


    if( i->second.arg ) {

      // option requires argument

      if( *++argv ) arguments[ name ] = *argv ;
      else
	throw std::runtime_error( "option " + s + " requires an argument" ) ;


    }

    // flag option

    else flags.insert( name ) ;

  }


}

command_line& cpl::util::operator>>( command_line& cl , std::string& s )
{
  if( !*cl.argv ) cl.state = false ;
  if( !cl.state ) return cl ;

  s = *cl.argv ;
  ++cl.argv ;
  return cl ;
}


bool cpl::util::command_line::is_set( std::string const& opt ) const
{

  bool arg = check_option( opt ) ;

  if( arg ) return arguments.count( opt ) != 0;
  else      return flags.count( opt ) != 0;

}


std::string cpl::util::command_line::get_arg( std::string const& opt ) const
{

  bool arg = check_option( opt ) ;

  if( !arg )
    throw std::runtime_error( "oops: option " + opt + " is a flag" ) ;

  std::map< std::string , std::string >::const_iterator i =
    arguments.find( opt ) ;

  if( i == arguments.end() )
    throw std::runtime_error( "option --" + opt + " required" ) ;

  return i->second ;

}


bool cpl::util::command_line::check_option( std::string const& opt ) const
{

  property_map::const_iterator i = props.find( opt ) ;

  if( i == props.end() )
    throw std::runtime_error( "oops: option " + opt + " doesn't exist" ) ;

  return i->second.arg ;

}
